Skip to content

Conversation

amirejaz
Copy link
Contributor

@amirejaz amirejaz commented Oct 8, 2025

Problem

Users were experiencing "Invalid redirect URI" errors during OAuth authentication when the specified callback port was unavailable. The issue occurred because:

  1. Port Resolution Timing: Dynamic client registration happened before port availability was checked, causing a mismatch between the registered callback URL and the actual port used
  2. Inconsistent Behavior: The system would silently pick a random port without clear indication to users
  3. Unpredictable Failures: Pre-registered clients would fail unexpectedly when ports were unavailable

Solution

This PR implements a comprehensive fix for OAuth callback port handling:

🔧 Core Changes

  • Port Resolution Before Registration: Moved port availability checking before dynamic client registration to ensure the OAuth provider is registered with the correct callback URL
  • Intelligent Port Behavior:
    • Dynamic Registration: Allows port fallback with clear warning messages
    • Pre-registered Clients: Uses strict port checking (fails if port unavailable)
  • Consistent Logging: Added informative messages when ports change

🧪 Testing

  • Added comprehensive tests for PerformOAuthFlow covering all port scenarios
  • Tests validate both success and failure cases
  • Mock OIDC discovery servers for reliable testing
  • Removed unused FindOrUsePortStrict function and related tests

📋 Behavior Summary

Scenario Port Specified Registration Type Behavior
No port specified 0 Dynamic ✅ Find available port
Port specified 8080 (available) Dynamic ✅ Use specified port
Port specified 8080 (unavailable) Dynamic ⚠️ Use alternative port with warning
Port specified 8080 (available) Pre-registered ✅ Use specified port
Port specified 8080 (unavailable) Pre-registered ❌ Fail with clear error

🎯 Benefits

  • Predictable: Explicitly specified ports are respected for pre-registered clients
  • Flexible: Dynamic registration allows port fallback when needed
  • Clear: Users get informative messages about port changes
  • Consistent: Same behavior across thv proxy and thv run commands
  • Robust: Comprehensive test coverage ensures reliability

Testing

  • ✅ All existing tests pass
  • ✅ New comprehensive test suite for port behavior
  • ✅ Manual testing with various port scenarios
  • ✅ Verified behavior with both dynamic and pre-registered OAuth clients

Breaking Changes

None. This is a bug fix that improves existing behavior without changing APIs.

Related Issues

Fixes the "Invalid redirect URI" error reported by users during OAuth authentication flows.

fixes #2098

Copy link

codecov bot commented Oct 8, 2025

Codecov Report

❌ Patch coverage is 47.22222% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.76%. Comparing base (5315e07) to head (2b3a080).

Files with missing lines Patch % Lines
pkg/networking/port.go 0.00% 16 Missing ⚠️
pkg/auth/discovery/discovery.go 85.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2121      +/-   ##
==========================================
+ Coverage   48.71%   48.76%   +0.05%     
==========================================
  Files         241      241              
  Lines       30594    30630      +36     
==========================================
+ Hits        14903    14938      +35     
+ Misses      14568    14562       -6     
- Partials     1123     1130       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ChrisJBurns
Copy link
Collaborator

@claude could you review this with a security hat on

Copy link
Contributor

claude bot commented Oct 8, 2025

Claude finished @ChrisJBurns's task —— View job


Security Review

I've completed a security-focused review of the OAuth callback port handling changes. Here are my findings:

Positive Security Aspects

  1. Port Validation Before Registration (Lines 398-420 in discovery.go)

    • Moving port resolution before dynamic client registration is correct - prevents OAuth provider registration with wrong callback URL
    • Eliminates TOCTOU (time-of-check-time-of-use) race condition between port check and registration
  2. Appropriate Port Behavior Differentiation

    • Dynamic registration: Allows port fallback (flexible, since callback URL can be registered dynamically)
    • Pre-registered clients: Strict port checking (correct, since IdP already has the callback URL configured)
  3. Good Port Selection Security (networking/port.go)

    • Uses cryptographically secure random number generation (crypto/rand) for port selection
    • Port range 10000-65535 avoids privileged ports (<1024)
    • Checks both TCP and UDP availability
  4. Clear User Communication

    • Warning messages when port fallback occurs (Line 410)
    • Explicit error messages for unavailable ports (Lines 417-419)
  5. Comprehensive Test Coverage

    • Tests validate both success and failure scenarios
    • Mock servers properly test OAuth flow without real credentials
    • Explicit tests for port checking logic

⚠️ Security Concerns & Recommendations

  1. Race Condition Risk in Port Checking (Critical)

    Issue: In IsAvailable() (networking/port.go:24-56), the function opens and immediately closes the port to check availability. Between the check and when the OAuth callback server actually binds (flow.go:184-189), another process could grab the port.

    // Port is checked here
    port, err := networking.FindOrUsePort(config.CallbackPort)
    // ... but server binding happens later
    f.server.ListenAndServe()  // Port might be taken now!

    Recommendation: For pre-registered clients, consider holding the listener open after the availability check, then passing it to the HTTP server. Example pattern:

    listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
    // ... later ...
    f.server.Serve(listener)  // Use existing listener
  2. Information Disclosure in Error Messages (Medium)

    Issue: Error messages reveal internal system state (discovery.go:417-419):

    return nil, fmt.Errorf(`specified auth callback port %d is not available
        - please choose a different port or ensure it's not in use`, config.CallbackPort)

    Recommendation: While helpful for debugging, consider whether revealing specific port availability to potential attackers is acceptable for your threat model. For defensive tools this is likely fine, but document this behavior.

  3. Missing Coverage for Lines (Low)

    Issue: Codecov shows 3 lines missing coverage (83.33% patch coverage). These are likely the warning/error logging paths.

    Recommendation: Add tests that verify warning messages are logged when port fallback occurs. Example:

    // Test that warning is logged when dynamic registration falls back to different port
  4. Port Exhaustion DoS Potential (Low)

    Issue: If an attacker can influence multiple OAuth flows simultaneously, they could cause port exhaustion by occupying many ports in the 10000-65535 range during callbacks.

    Recommendation: Consider adding rate limiting or connection pooling for OAuth flows if this is a concern in your deployment model.

  5. Localhost-Only Binding (Info)

    Observation: The OAuth callback server binds to all interfaces (:port in flow.go:185) rather than explicitly binding to localhost only.

    Recommendation: Consider binding to 127.0.0.1:port explicitly to prevent remote access to the callback server. While the state parameter provides protection, defense in depth is valuable:

    Addr: fmt.Sprintf("127.0.0.1:%d", f.port),

📊 Test Coverage Assessment

The new tests are well-structured:

  • ✅ Dynamic registration with available port
  • ✅ Dynamic registration with unavailable port (fallback)
  • ✅ Pre-registered client with available port
  • ✅ Pre-registered client with unavailable port (strict failure)
  • ✅ Port checking logic isolation

Missing test scenarios:

  • Warning message verification when port fallback occurs
  • Concurrent OAuth flow handling (if supported)
  • Edge case: port becomes unavailable between check and bind

🎯 Overall Security Assessment

Risk Level: Low to Medium

The changes are a security improvement overall. They fix the original "Invalid redirect URI" issue which could be exploited for denial-of-service by occupying the expected callback port. The differentiation between dynamic and pre-registered client behavior is architecturally sound.

The main concern is the TOCTOU race condition in port binding for pre-registered clients, which could lead to authentication failures (availability issue) or in very rare cases, a different application receiving the OAuth callback (security issue if that application is malicious and on the same system).

📝 Recommendations Priority

  1. High Priority: Fix TOCTOU race condition by holding listener open between check and server start
  2. Medium Priority: Add missing test coverage for warning/error paths
  3. Low Priority: Consider explicit localhost binding for defense in depth
  4. Low Priority: Document port availability information disclosure in security docs

Approval Recommendation

This PR is approved from a security perspective with the recommendation to address the TOCTOU race condition in a follow-up PR if not addressed here. The current implementation is significantly better than the previous behavior and the race condition window is narrow.


Job Run | Branch: fix_used_port

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remote MCP auth fails if remote auth callback port is in use

2 participants